using System;
using System.Runtime.InteropServices;
using System.Threading;
using System.IO;
using System.Windows.Forms;
using TerastarLib.Utils.Win32;

namespace XTCast.Mp3
{
	/// <summary>
	/// Summary description for Shark.
	/// </summary>
	public class Shark:IDisposable,IFrameReader
	{

		const int Shark_VID = 0x077d;
		const int Shark_PID = 0x627a;

		byte[] instatus = new byte[7];
		byte[] outstatus = new byte[]{0x00,0x80,0x00,0x00,0x00,0x00,0x00};
		byte[] outtune = new byte[]{0x00,0xC0,0x00,0x00,0x00,0x00,0x00};

		Thread mainThread;
		MP3EncodeLive _stream;
		bool quit;
		bool _attached=true;
		byte _band=0x12;
		int _channels,_bitrate;
		
		public Shark(int channels, int bitrate)
		{
			//assume it's attached until we find out otherwise
			_channels=channels;
			_bitrate=bitrate;
			Start();	
		}

		public void Start() 
		{
			quit=false;
			mainThread = new Thread(new ThreadStart(Run));
			mainThread.Start();
		}

		public void Stop() 
		{
			quit=true;
		}

		public int EncodeChannels 
		{
			get { return _channels; }
			set { _channels=value; Close(); }
		}

		public int EncodeBitrate
		{
			get { return _bitrate; }
			set { _bitrate=value; Close(); }
		}

		public bool Attached
		{
			get { return _attached; }
			set { _attached = value; if (!value) Close(); }
		}

		int _kHz;
		public int kHz
		{
			get { return _kHz; }
			set 
			{
				_band = 0x12;
				_kHz = value;

				outtune[2] = _band;
				outtune[3] = (byte)((_kHz+450)>>8);
				outtune[4] = (byte)((_kHz+450)&0xFF);
			}
		}

		float _mHz;
		public float mHz
		{
			get { return _mHz; }
			set 
			{ 
				_band = 0x2;
				_mHz = value;

				int val = (int)(((value-87.9) * 80)+7892);
				outtune[2] = _band;
				outtune[3] = (byte)(val>>8);
				outtune[4] = (byte)(val&0xFF);
			}
		}

		bool reading=false;
		private void DetectAndSetup() 
		{
			IntPtr hDevice;
			if(!HID.OpenHidDevice(out hDevice, Shark_VID, Shark_PID)) /* Open the HID device handle */
			{
				Attached=false;
				return;
			}
			FileStream fs = new FileStream(hDevice,FileAccess.ReadWrite,true,7,true);
			try 
			{
				Attached = true;
				while(true)
				{			
					if (outtune[2]!=instatus[2]||outtune[3]!=instatus[3]||outtune[4]!=instatus[4]) 
						fs.Write(outtune,0,7);						
					if (reading) 
						fs.Write(outstatus,0,7);
					else 
					{
						reading=true;
						fs.BeginRead(instatus,0,7,new AsyncCallback(readSomething),0);
					}
					if (quit) return;
					System.Threading.Thread.Sleep(2000);
				}
			}
			catch (IOException ex) 
			{
				Attached = false;
			}
			finally 
			{
				fs.Close();
			} 
		}

		private void Run() 
		{
			while(!quit) 
			{
				DetectAndSetup();
				if (!quit) Thread.Sleep(TimeSpan.FromSeconds(30));
			}
		}

		void readSomething(IAsyncResult ar)
		{
			reading=false;
			//System.Diagnostics.Trace.WriteLine(String.Format("{0} {1} {2} {3} {4} {5} {6}",Convert.ToString(instatus[0],16),Convert.ToString(instatus[1],16),Convert.ToString(instatus[2],16),Convert.ToString(instatus[3],16),Convert.ToString(instatus[4],16),Convert.ToString(instatus[5],16),Convert.ToString(instatus[6],16)));
		}
		
		public MP3EncodeLive BaseStream
		{
			get
			{
				if (!Attached) 
				{
					Close();
					throw new Exception("Shark is not attached!");
				}
				if (_stream==null) 
				{
					int sampleRate = 44100;
					if (_bitrate<=64000) sampleRate/=2;
					try 
					{
						_stream=new MP3EncodeLive(_bitrate, _channels, sampleRate);
						_stream.Start();
					} 
					catch (Exception ex) 
					{
						_stream=null;
						throw new Exception("Error w/ getting stream",ex);
					}

				}
				return _stream;
			}
		}

		public void Dispose()
		{
			Close();
			Stop();
		}

		public byte[] ReadFrames(FrameReadInfo info)
		{
			return BaseStream.ReadFrames(info);
		}

		public void SkipFrames(FrameReadInfo info)
		{
			BaseStream.SkipFrames(info);
		}

		public void Reset()
		{
			BaseStream.Reset();
		}

		public void Close()
		{
			lock(this) 	
			{
				if (_stream!=null) 
				{
					_stream.Close();
					_stream=null;
				}
			}
		}

		public int SampleRate
		{
			get
			{
				return BaseStream.SampleRate;
			}
		}

		int XTCast.Mp3.IFrameReader.Channels
		{
			get
			{
				return BaseStream.Channels;
			}
		}

		public int AvgBitrate
		{
			get
			{
				return BaseStream.AvgBitrate;
			}
		}
	}
}